var q = require("q");
var helper = require("./helper");
var md5 = require("md5");
var https = require("https");
var fs = require("fs");

var mch_api_hostname = "api.mch.weixin.qq.com";
var pay_api_paths = {
	//统一下单接口
	auth_token_url:"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
	unifiedorder_path:"/pay/unifiedorder",
	orderquery_path:"/pay/orderquery",
	closeorder_path:"/pay/closeorder",
	refund_path:"/secapi/pay/refund",
	refundquery_path:"/pay/refundquery",
	company_transfers_path:"/mmpaymkttransfers/promotion/transfers",
	company_get_transfers_path:"/mmpaymkttransfers/gettransferinfo"
};

/**
 *使用配置参数初始化对象,config对象包含下面的参数
 *1) appid 公众号ID
 *2) mch_id 商户Id
 *3) partner_key 商户密钥
 *4) cert pem证书的位置，即文件地址，如果不需要退款等功能可以不要
 *5) cert_key pem证书密钥的文件地址，如果设置了cert则应该提供此参数，否则会提示验证无法通过
 */ 
function WechatPayAPI(config){
	if(!(this instanceof WechatPayAPI)){
		return new WechatPayAPI(config);
	}

	if(config==undefined) throw new Error("必须提供参数对象");
	if(config.appid==undefined || config.mch_id==undefined ||config.partner_key==undefined) throw new Error("appid mch_id partner_key 三个参数必须提供");
	if(config.cert && !fs.existsSync(config.cert)) throw new Error("Cert文件不存在 ");   		
	if(config.cert_key && !fs.existsSync(config.cert_key)) throw new Error("Cert key 文件不存在");

	this.appid = config.appid;
	this.mch_id = config.mch_id;
	this.partner_key = config.partner_key;
	if(config.cert) this.cert = fs.readFileSync(config.cert);
	if(config.cert_key) this.cert_key = fs.readFileSync(config.cert_key);
}


/**
 * 为订单设置公共部分字段,包括appid,mch_id,nonce_str
 * @param {item} 需要设置公共字段的对象
 */
WechatPayAPI.prototype._set_common_field_to_item = function(item){
	item.appid = this.appid;
	item.mch_id = this.mch_id;
	item.nonce_str = helper.createNonceStr();
};

/**
 *JSAPI付款时所用公共字段设置,一般在公众号中使用JSAPI付款方式，统一下单成功后需要将数据返回到前端供
 *JSSDK作为参数调用，参数名称和普通对象也有所区别，这应该是微信接口开发人员不严谨造成的，如这里的
 *timeStamp中间的s必须大写，否则签名验证不通过
 *@param {item} 统一下单生成后为JSAPI返回的对象
 */
WechatPayAPI.prototype._set_pay_common_field_to_item = function(item) {
	item.appId = this.appid;
	item.timeStamp = helper.createTimestamp();
	item.nonceStr = helper.createNonceStr();
};

//在unifiedorder调用成功后返回JSAPI使用HTML页面调用使用的对象
WechatPayAPI.prototype._generate_pay_obj_for_jsapi = function(result){
	var item = {};
	this._set_pay_common_field_to_item(item);
	item.package = "prepay_id=" + result.prepay_id;
	item.signType = "MD5";
	item.paySign = helper.generate_pay_sign(item,this.partner_key); 
	return item;
};

//扫码成功后在回调页面处理完成后需要返回的数据格式
WechatPayAPI.prototype._generate_pay_obj_for_native = function(xml_obj){
	var item = {};
	this._set_common_field_to_item(item);
	return item;
};

//当unifiedorder统一下单接口调用成功后调用的方法
//与其它接口返回的结果不太一样，因为它需要返回给前端JS调用的对象
WechatPayAPI.prototype._do_when_unifiedorder_success = function(deferred,obj){
	if( obj.return_code === "FAIL"){
		deferred.reject(new Error("unifiedorder error," + obj.return_code + "," + obj.return_msg) );
	}else{
		if( obj.result_code ==="SUCCESS"){
			deferred.resolve(this._generate_pay_obj_for_jsapi(obj));
		}else{
			deferred.reject(new Error("unifiedorder error," + obj.err_code +"," + obj.err_code_des));
		}
	}
	return deferred;
};


//统一下单接口
WechatPayAPI.prototype.unifiedorder = function(order_item){
	var deferred = q.defer();
	this._set_common_field_to_item(order_item);
	order_item.sign = helper.generate_pay_sign(order_item,this.partner_key);
	var self = this;
	this.https_post_xml_str(order_item,pay_api_paths.unifiedorder_path).then(function(data){
		var result = {};
		//如果为JSAPI调用的方式，则返回给页面JS调用的对象需要多设置几个字段，具体参看调用方法
		if( order_item.trade_type ==="JSAPI"){
			deferred = self._do_when_unifiedorder_success(deferred,data);
		}else{
			deferred = self._do_when_call_success(deferred,data);	
		}
	}).fail(function(err){
		deferred.reject(new Error(err));
	});;
	return deferred.promise;
};


//当接口调用成功后调用的方法,根据返的XML文档判断实际业务逻辑的调用情况，失败则reject ，否则 resolve
WechatPayAPI.prototype._do_when_call_success = function(deferred,obj){
	//var obj = this._convert_xml_to_obj(xml_str);
	if( obj.return_code === "FAIL"){
		deferred.reject(new Error("Error," + obj.return_code + "," + obj.return_msg) );
	}else{
		if( obj.result_code ==="SUCCESS"){
			deferred.resolve(obj);
		}else{
			deferred.reject(new Error("Error," + obj.err_code +"," + obj.err_code_des));
		}
	}
	return deferred;
};


/**
 *使用https模块提交数据到服务器，这里需要配置证书的信息
 *@param {params} 需要提交到服务器的参数，此处为对象，提交前会被转化为XML字符串
 *@param {api_path} 微信接口的路径，和服务器域名组合在一起形成访问地址
 *@returns 返回一个promise，调用成功后返回的值为响应的对象，原生返回的值为XML字符串，转码为对象，每
 *个对象的字段名称与具体的接口相关，具体请参考微信支付的接口返回值
 */ 
WechatPayAPI.prototype.https_post_xml_str = function(params,api_path){
	var xml_str = helper.convert_obj_to_xml(params);
	var deferred = q.defer();
	var options = {
		hostname:mch_api_hostname,
		port:443,
		path:api_path,
		method:"POST",
		key:this.cert_key,//fs.readFileSync("certs/apiclient_key.pem"),
		cert:this.cert,//fs.readFileSync("certs/apiclient_cert.pem"),
		headers:{
			"Content-Type":"application/xml"
		}
	};
	var req = https.request(options,function(res){
		res.setEncoding("utf8");
		var data = "";
		res.on("data",function(chunk){
			data = data + chunk;
		});
		res.on("end",function(){
			deferred.resolve(helper.convert_xml_to_obj(data));
		});
		res.on("error",function(error){
			deferred.reject(new Error(error));
		});
	});
	req.write(xml_str);
	req.end();
	return deferred.promise;
};

/**
 *提交数据的能用方法，大部分接口方法中都需要设置公共参数和生成sign，放在一个公共方法中即可
 *@param {params} 要提交的JSON对象
 *@param {path}   提交路径
 *@param {set_common_fields} 设置公共参数方法，如果为空则默认为this._set_common_field_to_item方法,大部分接口都使用的此方法
 *@return 返回promise 对象
 */
WechatPayAPI.prototype.post_data = function(params,path,set_common_fields){
	var deferred = q.defer();

	if(set_common_fields == undefined ) set_common_fields = this._set_common_field_to_item;

	set_common_fields.bind(this)(params);
	params.sign = helper.generate_pay_sign(params,this.partner_key);
	console.log(params);
	this.https_post_xml_str(params,path).then(function(data){
		deferred.resolve(data);
	}).fail(function(err){
		deferred.reject(new Error(err));	
	});
	return deferred.promise;
};

/**
 *获取订单状态,参数对象应该包括下面参数中的至少一个,transaction_id 或者 out_trade_no
 *@param {params} 查询参数，至少包括transaction_id\out_trade_no中的一个,transaction_id 微信单号,out_trade_no 业务单号
 */
WechatPayAPI.prototype.orderquery = function(params){
	return this.post_data(params,pay_api_paths.orderquery_path);
};

//关闭订单功能
WechatPayAPI.prototype.closeorder = function(params){
	return this.post_data(params,pay_api_paths.closeorder_path);
};

//订单退款
WechatPayAPI.prototype.refund = function(params){
	return this.post_data(params,pay_api_paths.refundquery_path);
};

//查询退款
WechatPayAPI.prototype.refundquery = function(params){
	return this.post_data(params,pay_api_paths.refundquery_path);
};

//在网页上扫码支付的时候需先根据产品或订单号生成二维码
WechatPayAPI.prototype.generate_barcode_url = function(product_id){
	var params = {product_id:product_id};
	this._set_common_field_to_item(params);
	params.time_stamp = helper.createTimestamp();
	params.sign = helper.generate_pay_sign(params,this.partner_key);
	//此处的排序只为美观，非必要
	var url = "weixin://wxpay/bizpayurl?"+ Object.keys(params).sort().map(function(key){return key + "=" + params[key];}).join("&"); 
	return url;
};

/**
 *扫码成功后在回调页面处理完成后需要返回的数据格式为XML字符串
 *@param {item} 回调页面返回给微信服务器的对象，返回前需要先将其转化为XML字符串
**/
WechatPayAPI.prototype.generate_barcode_back_data = function(item){
	//如果返回对象已经出错，则直接将错误值返回即可
	this._set_common_field_to_item(item);
	item.sign = helper.generate_pay_sign(item,this.partner_key); 
	return helper.convert_obj_to_xml(item);
};

/************* 企业付款功能 start ***************/
/**
 *为企业付款接口对象设置公共字段，如mch_appid mchid nonce_str等信息，因为参数名称和上面其他接口不一样
 *所以不能共用
 *@param {params} 企业付款对象
 */
WechatPayAPI.prototype._set_company_transfers_common_field = function(params){
	params.mch_appid = this.appid;
	params.mchid = this.mch_id;
	params.nonce_str = helper.createNonceStr();
};


/**
 *企业付款接口，企业向用户付款，需要确保有可用余额.
 *@param {params} 付款对象，包含openid即接收方的openid值
 */
WechatPayAPI.prototype.company_transfers = function(params){
	return this.post_data(params,pay_api_paths.company_transfers_path,this._set_company_transfers_common_field);
};

/**
 *企业付款情况功能查询,根据商户订单号查询。微信开发的人也是醉了，查询接口和退款接口的参数竟然他妈不
 *一样，说好的规范呢?
 *@param {params} 查询参数，除公共参数外主要是partner_trade_no字段，即商户订单号
 */ 
WechatPayAPI.prototype.company_get_transfers = function(params){
	return this.post_data(params,pay_api_paths.company_get_transfers_path);
};

/************* 企业付款功能 end ***************/

exports.WechatPayAPI = WechatPayAPI;
